/**
  ******************************************************************************
  * @file    mathx_base.c
  * @author  siyun.chen
  * @version v1.0.0
  * @date
  * @brief   适用于嵌入式端的C语言数学拓展库
  ******************************************************************************
  * Change Logs:
  * 2025-07-30      siyun.chen      first version
  ******************************************************************************
  *
  * MIT License
  *
  * Copyright (c) 2025 siyun.chen
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
  * in the Software without restriction, including without limitation the rights
  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  * copies of the Software, and to permit persons to whom the Software is
  * furnished to do so, subject to the following conditions:
  *
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
  *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  ******************************************************************************
  */
#include "mathx_base.h"

/**
 * @brief 在uint16_t类型数组中查找最小值
 *
 * @param pSrc      输入数组的指针
 * @param blockSize 输入数组的长度
 * @param pResult   返回最小值, 如果为NULL则不会返回该值
 * @param pIndex    返回最小值对应的索引, 如果为NULL则不会返回该值
 *
 * @return 最小值
 */
uint16_t mathx_min_u16(const uint16_t *pSrc, uint32_t blockSize, uint16_t *pResult, uint32_t *pIndex)
{
  uint16_t min = pSrc[0];
  uint32_t index = 0;

  for (uint32_t i = 1; i < blockSize; i++)
  {
    if (pSrc[i] < min)
    {
      min = pSrc[i];
      index = i;
    }
  }

  if (pResult)
    *pResult = min;
  if (pIndex)
    *pIndex = index;

  return min;
}

/**
 * @brief 在uint32_t类型数组中查找最小值
 *
 * @param pSrc      输入数组的指针
 * @param blockSize 输入数组的长度
 * @param pResult   返回最小值, 如果为NULL则不会返回该值
 * @param pIndex    返回最小值对应的索引, 如果为NULL则不会返回该值
 *
 * @return 最小值
 */
uint32_t mathx_min_u32(const uint32_t *pSrc, uint32_t blockSize, uint32_t *pResult, uint32_t *pIndex)
{
  uint32_t min = pSrc[0];
  uint32_t index = 0;

  for (uint32_t i = 1; i < blockSize; i++)
  {
    if (pSrc[i] < min)
    {
      min = pSrc[i];
      index = i;
    }
  }

  if (pResult)
    *pResult = min;
  if (pIndex)
    *pIndex = index;

  return min;
}

/**
 * @brief 在float类型数组中查找最小值
 *
 * @param pSrc      输入数组的指针
 * @param blockSize 输入数组的长度
 * @param pResult   返回最小值, 如果为NULL则不会返回该值
 * @param pIndex    返回最小值对应的索引, 如果为NULL则不会返回该值
 *
 * @return 最小值
 */
float mathx_min_f32(const float *pSrc, uint32_t blockSize, float *pResult, uint32_t *pIndex)
{
  float min = pSrc[0];
  uint32_t index = 0;

  for (uint32_t i = 1; i < blockSize; i++)
  {
    if (pSrc[i] < min)
    {
      min = pSrc[i];
      index = i;
    }
  }

  if (pResult)
    *pResult = min;
  if (pIndex)
    *pIndex = index;

  return min;
}

/**
 * @brief 在double类型数组中查找最小值
 *
 * @param pSrc      输入数组的指针
 * @param blockSize 输入数组的长度
 * @param pResult   返回最小值, 如果为NULL则不会返回该值
 * @param pIndex    返回最小值对应的索引, 如果为NULL则不会返回该值
 *
 * @return 最小值
 */
double mathx_min_f64(const double *pSrc, uint32_t blockSize, double *pResult, uint32_t *pIndex)
{
  double min = pSrc[0];
  uint32_t index = 0;

  for (uint32_t i = 1; i < blockSize; i++)
  {
    if (pSrc[i] < min)
    {
      min = pSrc[i];
      index = i;
    }
  }

  if (pResult)
    *pResult = min;
  if (pIndex)
    *pIndex = index;

  return min;
}

/**
 * @brief 在uint16_t类型数组中查找最大值
 *
 * @param pSrc      输入数组的指针
 * @param blockSize 输入数组的长度
 * @param pResult   返回最大值, 如果为NULL则不会返回该值
 * @param pIndex    返回最大值对应的索引, 如果为NULL则不会返回该值
 *
 * @return 最大值
 */
uint16_t mathx_max_u16(const uint16_t *pSrc, uint32_t blockSize, uint16_t *pResult, uint32_t *pIndex)
{
  uint16_t max = pSrc[0];
  uint32_t index = 0;

  for (uint32_t i = 1; i < blockSize; i++)
  {
    if (pSrc[i] > max)
    {
      max = pSrc[i];
      index = i;
    }
  }

  if (pResult)
    *pResult = max;
  if (pIndex)
    *pIndex = index;

  return max;
}

/**
 * @brief 在uint32_t类型数组中查找最大值
 *
 * @param pSrc      输入数组的指针
 * @param blockSize 输入数组的长度
 * @param pResult   返回最大值, 如果为NULL则不会返回该值
 * @param pIndex    返回最大值对应的索引, 如果为NULL则不会返回该值
 *
 * @return 最大值
 */
uint32_t mathx_max_u32(const uint32_t *pSrc, uint32_t blockSize, uint32_t *pResult, uint32_t *pIndex)
{
  uint32_t max = pSrc[0];
  uint32_t index = 0;

  for (uint32_t i = 1; i < blockSize; i++)
  {
    if (pSrc[i] > max)
    {
      max = pSrc[i];
      index = i;
    }
  }

  if (pResult)
    *pResult = max;
  if (pIndex)
    *pIndex = index;

  return max;
}

/**
 * @brief 在float类型数组中查找最大值
 *
 * @param pSrc      输入数组的指针
 * @param blockSize 输入数组的长度
 * @param pResult   返回最大值, 如果为NULL则不会返回该值
 * @param pIndex    返回最大值对应的索引, 如果为NULL则不会返回该值
 *
 * @return 最大值
 */
float mathx_max_f32(const float *pSrc, uint32_t blockSize, float *pResult, uint32_t *pIndex)
{
  float max = pSrc[0];
  uint32_t index = 0;

  for (uint32_t i = 1; i < blockSize; i++)
  {
    if (pSrc[i] > max)
    {
      max = pSrc[i];
      index = i;
    }
  }

  if (pResult)
    *pResult = max;
  if (pIndex)
    *pIndex = index;

  return max;
}

/**
 * @brief 在double类型数组中查找最大值
 *
 * @param pSrc      输入数组的指针
 * @param blockSize 输入数组的长度
 * @param pResult   返回最大值, 如果为NULL则不会返回该值
 * @param pIndex    返回最大值对应的索引, 如果为NULL则不会返回该值
 *
 * @return 最大值
 */
double mathx_max_f64(const double *pSrc, uint32_t blockSize, double *pResult, uint32_t *pIndex)
{
  double max = pSrc[0];
  uint32_t index = 0;

  for (uint32_t i = 1; i < blockSize; i++)
  {
    if (pSrc[i] > max)
    {
      max = pSrc[i];
      index = i;
    }
  }

  if (pResult)
    *pResult = max;
  if (pIndex)
    *pIndex = index;

  return max;
}

/**
 * @brief 计算uint16_t类型数组之和
 *
 * @param pSrc      输入数组的指针
 * @param blockSize 输入数组的大小
 *
 * @return 数组之和
 */
uint64_t mathx_sum_u16(const uint16_t *pSrc, uint32_t blockSize)
{
  uint64_t sum = 0;
  for (uint32_t i = 0; i < blockSize; i++)
  {
    sum += pSrc[i];
  }
  return sum;
}

/**
 * @brief 计算uint32_t类型数组之和
 *
 * @param pSrc      输入数组的指针
 * @param blockSize 输入数组的大小
 *
 * @return 数组之和
 */
uint64_t mathx_sum_u32(const uint32_t *pSrc, uint32_t blockSize)
{
  uint64_t sum = 0;
  for (uint32_t i = 0; i < blockSize; i++)
  {
    sum += pSrc[i];
  }
  return sum;
}

/**
 * @brief 计算float类型数组之和
 *
 * @param pSrc      输入数组的指针
 * @param blockSize 输入数组的大小
 *
 * @return 数组之和
 */
double mathx_sum_f32(const float *pSrc, uint32_t blockSize)
{
  double sum = 0;
  for (uint32_t i = 0; i < blockSize; i++)
  {
    sum += pSrc[i];
  }
  return sum;
}

/**
 * @brief 计算double类型数组之和
 *
 * @param pSrc      输入数组的指针
 * @param blockSize 输入数组的大小
 *
 * @return 数组之和
 */
double mathx_sum_f64(const double *pSrc, uint32_t blockSize)
{
  double sum = 0;
  for (uint32_t i = 0; i < blockSize; i++)
  {
    sum += pSrc[i];
  }
  return sum;
}

/**
 * @brief 计算uint16_t类型数组平均值
 *
 * @param pSrc      输入数组的指针
 * @param blockSize 输入数组的大小
 * @param pResult   返回对应类型平均值(如果是整数类型会进行四舍五入操作), 如果为NULL则不会返回该值
 *
 * @return double类型平均值
 */
double mathx_avg_u16(const uint16_t *pSrc, uint32_t blockSize, uint16_t *pResult)
{
  double avg = 0.0;
  for (uint32_t i = 0; i < blockSize; i++)
  {
    avg += pSrc[i];
  }
  avg = avg / blockSize;

  if (pResult)
    *pResult = (uint16_t)(avg + 0.5);

  return avg;
}

/**
 * @brief 计算uint32_t类型数组平均值
 *
 * @param pSrc      输入数组的指针
 * @param blockSize 输入数组的大小
 * @param pResult   返回对应类型平均值(如果是整数类型会进行四舍五入操作), 如果为NULL则不会返回该值
 *
 * @return double类型平均值
 */
double mathx_avg_u32(const uint32_t *pSrc, uint32_t blockSize, uint32_t *pResult)
{
  double avg = 0.0;
  for (uint32_t i = 0; i < blockSize; i++)
  {
    avg += pSrc[i];
  }
  avg = avg / blockSize;

  if (pResult)
    *pResult = (uint32_t)(avg + 0.5);

  return avg;
}

/**
 * @brief 计算float类型数组平均值
 *
 * @param pSrc      输入数组的指针
 * @param blockSize 输入数组的大小
 * @param pResult   返回对应类型平均值(如果是整数类型会进行四舍五入操作), 如果为NULL则不会返回该值
 *
 * @return double类型平均值
 */
double mathx_avg_f32(const float *pSrc, uint32_t blockSize, float *pResult)
{
  double avg = 0.0;
  for (uint32_t i = 0; i < blockSize; i++)
  {
    avg += pSrc[i];
  }
  avg = avg / blockSize;

  if (pResult)
    *pResult = (float)avg;

  return avg;
}

/**
 * @brief 计算double类型数组平均值
 *
 * @param pSrc      输入数组的指针
 * @param blockSize 输入数组的大小
 * @param pResult   返回对应类型平均值(如果是整数类型会进行四舍五入操作), 如果为NULL则不会返回该值
 *
 * @return double类型平均值
 */
double mathx_avg_f64(const double *pSrc, uint32_t blockSize, double *pResult)
{
  double avg = 0.0;
  for (uint32_t i = 0; i < blockSize; i++)
  {
    avg += pSrc[i];
  }
  avg = avg / blockSize;

  if (pResult)
    *pResult = avg;

  return avg;
}

static int compare_u16(const void *a, const void *b)
{ // 升序
  if (*(uint16_t *)a > *(uint16_t *)b)
    return 1;
  if (*(uint16_t *)a < *(uint16_t *)b)
    return -1;
  return 0;
}

/**
 * @brief 获取uint16_t类型数组中位数
 * 
 * @note  此函数会通过调用qsort函数对输入数组进行升序排序，因此会改变原数组的元素顺序。
 * 
 * @param pSrc      输入数组的指针
 * @param blockSize 输入数组的大小
 * @param pResult   返回对应类型中位数(如果是整数类型会进行四舍五入操作), 如果为NULL则不会返回该值
 * 
 * @return 浮点类型中位数
 */
float mathx_median_u16(uint16_t *pSrc, uint32_t blockSize, uint16_t *pResult)
{
  if (blockSize == 0 || pSrc == NULL)
    return 0.0;

  qsort(pSrc, blockSize, sizeof(pSrc[0]), compare_u16);

  float median = 0.0;
  if (blockSize % 2 == 0)
    median = (pSrc[blockSize / 2 - 1] + pSrc[blockSize / 2]) / 2.0;
  else
    median = pSrc[blockSize / 2];

  if (pResult)
    *pResult = (uint16_t)(median + 0.5f);

  return median;
}

static int compare_u32(const void *a, const void *b)
{ // 升序
  if (*(uint32_t *)a > *(uint32_t *)b)
    return 1;
  if (*(uint32_t *)a < *(uint32_t *)b)
    return -1;
  return 0;
}

/**
 * @brief 获取uint32_t类型数组中位数
 * 
 * @note  此函数会通过调用qsort函数对输入数组进行升序排序，因此会改变原数组的元素顺序。
 * 
 * @param pSrc      输入数组的指针
 * @param blockSize 输入数组的大小
 * @param pResult   返回对应类型中位数(如果是整数类型会进行四舍五入操作), 如果为NULL则不会返回该值
 * 
 * @return 浮点类型中位数
 */
float mathx_median_u32(uint32_t *pSrc, uint32_t blockSize, uint32_t *pResult)
{
  if (blockSize == 0 || pSrc == NULL)
    return 0.0;

  qsort(pSrc, blockSize, sizeof(pSrc[0]), compare_u32);

  float median = 0.0;
  if (blockSize % 2 == 0)
    median = (pSrc[blockSize / 2 - 1] + pSrc[blockSize / 2]) / 2.0;
  else
    median = pSrc[blockSize / 2];

  if (pResult)
    *pResult = (uint32_t)(median + 0.5f);

  return median;
}

static int compare_f32(const void *a, const void *b)
{ // 升序
  if (*(float *)a > *(float *)b)
    return 1;
  if (*(float *)a < *(float *)b)
    return -1;
  return 0;
}

/**
 * @brief 获取float类型数组中位数
 * 
 * @note  此函数会通过调用qsort函数对输入数组进行升序排序，因此会改变原数组的元素顺序。
 * 
 * @param pSrc      输入数组的指针
 * @param blockSize 输入数组的大小
 * @param pResult   返回对应类型中位数(如果是整数类型会进行四舍五入操作), 如果为NULL则不会返回该值
 * 
 * @return 浮点类型中位数
 */
float mathx_median_f32(float *pSrc, uint32_t blockSize, float *pResult)
{
  if (blockSize == 0 || pSrc == NULL)
    return 0.0;

  qsort(pSrc, blockSize, sizeof(pSrc[0]), compare_f32);

  float median = 0.0;
  if (blockSize % 2 == 0)
    median = (pSrc[blockSize / 2 - 1] + pSrc[blockSize / 2]) / 2.0f;
  else
    median = pSrc[blockSize / 2];

  if (pResult)
    *pResult = median;

  return median;
}

static int compare_f64(const void *a, const void *b)
{ // 升序
  if (*(double *)a > *(double *)b)
    return 1;
  if (*(double *)a < *(double *)b)
    return -1;
  return 0;
}

/**
 * @brief 获取double类型数组中位数
 * 
 * @note  此函数会通过调用qsort函数对输入数组进行升序排序，因此会改变原数组的元素顺序。
 * 
 * @param pSrc      输入数组的指针
 * @param blockSize 输入数组的大小
 * @param pResult   返回对应类型中位数(如果是整数类型会进行四舍五入操作), 如果为NULL则不会返回该值
 * 
 * @return 浮点类型中位数
 */
double mathx_median_f64(double *pSrc, uint32_t blockSize, double *pResult)
{
  if (blockSize == 0 || pSrc == NULL)
    return 0.0;

  qsort(pSrc, blockSize, sizeof(pSrc[0]), compare_f64);

  double median = 0.0;
  if (blockSize % 2 == 0)
    median = (pSrc[blockSize / 2 - 1] + pSrc[blockSize / 2]) / 2.0;
  else
    median = pSrc[blockSize / 2];

  if (pResult)
      *pResult = median;

  return median;
}

/**
 * @brief 计算uint16_t类型总体标准差
 *
 * @param pSrc      输入数组的指针
 * @param blockSize 输入数组的大小
 * @param pResult   返回总体标准差(如果是整数类型会进行四舍五入操作), 如果为NULL则不会返回该值
 *
 * @return 总体标准差
 */
float mathx_stdevP_u16(uint16_t *pSrc, uint32_t blockSize, float *pResult)
{
  double avg = mathx_avg_u16(pSrc, blockSize, NULL);

  double variance = 0.0;
  for(int i = 0; i < blockSize; i++)
  {
    variance += pow(pSrc[i] - avg, 2);
  }
  
  double stdev = sqrt(variance / blockSize);
  
  if (pResult)
    *pResult = (float)stdev;

  return stdev;
}

/**
 * @brief 计算uint32_t类型总体标准差
 *
 * @param pSrc      输入数组的指针
 * @param blockSize 输入数组的大小
 * @param pResult   返回总体标准差(如果是整数类型会进行四舍五入操作), 如果为NULL则不会返回该值
 *
 * @return 总体标准差
 */
float mathx_stdevP_u32(uint32_t *pSrc, uint32_t blockSize, float *pResult)
{
  double avg = mathx_avg_u32(pSrc, blockSize, NULL);

  double variance = 0.0;
  for(int i = 0; i < blockSize; i++)
  {
    variance += pow(pSrc[i] - avg, 2);
  }
  
  double stdev = sqrt(variance / blockSize);
  
  if (pResult)
    *pResult = (float)stdev;

  return stdev;
}

/**
 * @brief 计算float类型总体标准差
 *
 * @param pSrc      输入数组的指针
 * @param blockSize 输入数组的大小
 * @param pResult   返回总体标准差(如果是整数类型会进行四舍五入操作), 如果为NULL则不会返回该值
 *
 * @return 总体标准差
 */
float mathx_stdevP_f32(float *pSrc, uint32_t blockSize, float *pResult)
{
  double avg = mathx_avg_f32(pSrc, blockSize, NULL);

  double variance = 0.0;
  for(int i = 0; i < blockSize; i++)
  {
    variance += pow(pSrc[i] - avg, 2);
  }
  
  double stdev = sqrt(variance / blockSize);
  
  if (pResult)
    *pResult = (float)stdev;

  return stdev;
}

/**
 * @brief 计算double类型总体标准差
 *
 * @param pSrc      输入数组的指针
 * @param blockSize 输入数组的大小
 * @param pResult   返回总体标准差(如果是整数类型会进行四舍五入操作), 如果为NULL则不会返回该值
 *
 * @return 总体标准差
 */
float mathx_stdevP_f64(double *pSrc, uint32_t blockSize, float *pResult)
{
  double avg = mathx_avg_f64(pSrc, blockSize, NULL);

  double variance = 0.0;
  for(int i = 0; i < blockSize; i++)
  {
    variance += pow(pSrc[i] - avg, 2);
  }
  
  double stdev = sqrt(variance / blockSize);
  
  if (pResult)
    *pResult = (float)stdev;

  return stdev;
}

/**
 * @brief 计算uint16_t类型样本标准差
 *
 * @param pSrc      输入数组的指针
 * @param blockSize 输入数组的大小
 * @param pResult   返回样本标准差, 如果为NULL则不会返回该值
 *
 * @return 样本标准差
 */
float mathx_stdevS_u16(uint16_t *pSrc, uint32_t blockSize, float *pResult)
{
  double avg = mathx_avg_u16(pSrc, blockSize, NULL);

  double variance = 0.0;
  for(int i = 0; i < blockSize; i++)
  {
    variance += pow(pSrc[i] - avg, 2);
  }
  
  double stdev = sqrt(variance / (blockSize - 1));
  
  if (pResult)
    *pResult = (float)stdev;

  return stdev;
}

/**
 * @brief 计算uint32_t类型样本标准差
 *
 * @param pSrc      输入数组的指针
 * @param blockSize 输入数组的大小
 * @param pResult   返回样本标准差, 如果为NULL则不会返回该值
 *
 * @return 样本标准差
 */
float mathx_stdevS_u32(uint32_t *pSrc, uint32_t blockSize, float *pResult)
{
  double avg = mathx_avg_u32(pSrc, blockSize, NULL);

  double variance = 0.0;
  for(int i = 0; i < blockSize; i++)
  {
    variance += pow(pSrc[i] - avg, 2);
  }
  
  double stdev = sqrt(variance / (blockSize - 1));
  
  if (pResult)
    *pResult = (float)stdev;

  return stdev;
}

/**
 * @brief 计算float类型样本标准差
 *
 * @param pSrc      输入数组的指针
 * @param blockSize 输入数组的大小
 * @param pResult   返回样本标准差(如果是整数类型会进行四舍五入操作), 如果为NULL则不会返回该值
 *
 * @return 样本标准差
 */
float mathx_stdevS_f32(float *pSrc, uint32_t blockSize, float *pResult)
{
  double avg = mathx_avg_f32(pSrc, blockSize, NULL);

  double variance = 0.0;
  for(int i = 0; i < blockSize; i++)
  {
    variance += pow(pSrc[i] - avg, 2);
  }
  
  double stdev = sqrt(variance / (blockSize - 1));
  
  if (pResult)
    *pResult = (float)stdev;

  return stdev;
}

/**
 * @brief 计算double类型样本标准差
 *
 * @param pSrc      输入数组的指针
 * @param blockSize 输入数组的大小
 * @param pResult   返回样本标准差(如果是整数类型会进行四舍五入操作), 如果为NULL则不会返回该值
 *
 * @return 样本标准差
 */
float mathx_stdevS_f64(double *pSrc, uint32_t blockSize, float *pResult)
{
  double avg = mathx_avg_f64(pSrc, blockSize, NULL);

  double variance = 0.0;
  for(int i = 0; i < blockSize; i++)
  {
    variance += pow(pSrc[i] - avg, 2);
  }
  
  double stdev = sqrt(variance / (blockSize - 1));
  
  if (pResult)
    *pResult = (float)stdev;

  return stdev;
}
